package com.anji.plus.gaea.job.executor.thread;

import com.anji.plus.gaea.job.core.dto.ReturnT;
import com.anji.plus.gaea.job.core.param.RegistryParam;
import com.anji.plus.gaea.job.executor.config.ExecutorConfiguration;
import com.anji.plus.gaea.job.executor.config.ExecutorProperties;
import com.anji.plus.gaea.job.executor.handler.JobHandler;
import com.anji.plus.gaea.job.executor.service.TriggerClient;
import com.anji.plus.gaea.job.executor.util.IpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;

import java.util.concurrent.TimeUnit;

/**
 * Created by xuxueli on 17/3/2.
 *
 * Borrowed from xxljob v2.4.0
 */
public class ExecutorRegistryThread {
    private static Logger logger = LoggerFactory.getLogger(ExecutorRegistryThread.class);

    private static ExecutorRegistryThread instance = new ExecutorRegistryThread();
    public static ExecutorRegistryThread getInstance(){
        return instance;
    }

    private Thread registryThread;

    private ApplicationContext applicationContext;

    private ExecutorProperties executorProperties;

    /** 所有job的实现类beanname */
    private String[] jobHandlerBeanNames;

    private volatile boolean toStop = false;

    public void start(){
        // 扫描所有JobHandler的实现
        jobHandlerBeanNames = applicationContext.getBeanNamesForType(JobHandler.class, false, true);
        String port = applicationContext.getEnvironment().getProperty("server.port");
        String contextPath = applicationContext.getEnvironment().getProperty("server.servlet.context-path");
        String ip = IpUtil.getIp();
        String httpAddress = "";
        if(contextPath != null && contextPath.equals("/") == false && contextPath.length() > 1){
            httpAddress = String.format("http://%s:%s/%s/", ip, port, contextPath);
        } else {
            httpAddress = String.format("http://%s:%s/", ip, port);
        }
        final String executorCode = applicationContext.getEnvironment().getProperty("spring.application.name");
        final String executorAddress = httpAddress;

        // valid
        if (executorCode==null || executorCode.trim().length()==0) {
            logger.warn(">>>>>>>>>>> xxl-job, executor registry config fail, spring.application.name is null.");
            return;
        }
        if (ExecutorConfiguration.getTriggerClientList() == null) {
            logger.warn(">>>>>>>>>>> xxl-job, executor registry config fail, TriggerClientList is null.");
            return;
        }

        registryThread = new Thread(new Runnable() {
            @Override
            public void run() {
                // registry
                while (!toStop) {
                    try {
                        RegistryParam registryParam = new RegistryParam(executorCode, executorAddress, jobHandlerBeanNames);
                        for(TriggerClient triggerClient: ExecutorConfiguration.getTriggerClientList()){
                            try {
                                ReturnT<String> registryResult = triggerClient.registryUp(registryParam);
                                if (registryResult!=null && ReturnT.SUCCESS_CODE == registryResult.getCode()) {
                                    registryResult = ReturnT.SUCCESS;
                                    logger.debug(">>>>>>>>>>> xxl-job registry success, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
                                    break;
                                } else {
                                    logger.info(">>>>>>>>>>> xxl-job registry fail, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
                                }
                            } catch (Exception e) {
                                logger.info(">>>>>>>>>>> xxl-job registry error, registryParam:{}", registryParam, e);
                            }
                        }
                    } catch (Exception e) {
                        if (!toStop) {
                            logger.error(e.getMessage(), e);
                        }

                    }

                    try {
                        if (!toStop) {
                            TimeUnit.SECONDS.sleep(executorProperties.getBeatTimeoutSeconds());
                        }
                    } catch (InterruptedException e) {
                        if (!toStop) {
                            logger.warn(">>>>>>>>>>> xxl-job, executor registry thread interrupted, error msg:{}", e.getMessage());
                        }
                    }
                }

                // registry remove
                try {
                    RegistryParam registryParam = new RegistryParam(executorCode, executorAddress);
                    for(TriggerClient triggerClient: ExecutorConfiguration.getTriggerClientList()){
                        try {
                            ReturnT<String> registryResult = triggerClient.registryDown(registryParam);
                            if (registryResult!=null && ReturnT.SUCCESS_CODE == registryResult.getCode()) {
                                registryResult = ReturnT.SUCCESS;
                                logger.info(">>>>>>>>>>> xxl-job registry-remove success, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
                                break;
                            } else {
                                logger.info(">>>>>>>>>>> xxl-job registry-remove fail, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
                            }
                        } catch (Exception e) {
                            if (!toStop) {
                                logger.info(">>>>>>>>>>> xxl-job registry-remove error, registryParam:{}", registryParam, e);
                            }
                        }
                    }
                } catch (Exception e) {
                    if (!toStop) {
                        logger.error(e.getMessage(), e);
                    }
                }
                logger.info(">>>>>>>>>>> xxl-job, executor registry thread destroy.");

            }
        });
        registryThread.setDaemon(true);
        registryThread.setName("xxl-job, executor ExecutorRegistryThread");
        registryThread.start();
    }


    public void toStop() {
        toStop = true;

        // interrupt and wait
        if (registryThread != null) {
            registryThread.interrupt();
            try {
                registryThread.join();
            } catch (InterruptedException e) {
                logger.error(e.getMessage(), e);
            }
        }
    }

    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    public void setExecutorProperties(ExecutorProperties executorProperties) {
        this.executorProperties = executorProperties;
    }
}
